import hashlib
import hmac
import os
import random
import re
import string
import time
from typing import Union

import requests
from django.conf import settings


class PhoneValidationError(Exception):
    def __str__(self):
        return "手机号码不符合规则"


class SmsSendFail(Exception):
    pass


# Documentation: https://authdata.geetest.com/docs/message.html
HOST = "https://tectapi.geetest.com/message"
MAX_VAR_VALUE_LENGTH = 32


def gen_headers():
    envs = os.environ
    assert envs.get("GT_ID") and envs.get("GT_KEY"), "请先在.env配置极验密钥"
    gt_id = envs["GT_ID"]
    chars = string.digits + string.ascii_lowercase
    nonce = "".join(random.choice(chars) for _ in range(32))
    timestamp = str(int(time.time()))
    message = "".join(sorted((gt_id, timestamp, nonce))).encode()
    secret = envs["GT_KEY"].encode()
    signature = hmac.new(secret, message, hashlib.sha256).hexdigest()
    auth = "gt_id={},nonce={},signature={},timestamp={}"
    return {"Authorization": auth.format(gt_id, nonce, signature, timestamp)}


# 已经在极验设置，并且审核通过的一些模板
ZHSQKJ_VCODE_TEMPLATE = 100_266
ZHSQKJ_SIGNATURE = "【智慧社区科技】"
VCODE_CONTENT = "验证码#code#，您本次操作，需要进行校验。请勿泄漏您的验证码。"
PAY_TEMPLATE = 100_457
PAY_SIGNATURE = "【布吉中心花园】"
PAY_CONTENT = "尊敬的#room#业主/住户：您的账单：#billitem#，金额:#total#元，点击缴费#paylink#"

template_content = {
    ZHSQKJ_VCODE_TEMPLATE: ZHSQKJ_SIGNATURE + VCODE_CONTENT,
    PAY_TEMPLATE: PAY_SIGNATURE + PAY_CONTENT,
}


def build_content(content: str, **kwargs: str) -> str:
    if "#" not in content:
        return content
    return re.sub(r"#([a-z]+)#", r"{\1}", content).format(**kwargs)


# # Deprecated:
# gt_mode = {
#     "vcode": VCODE_TEMPLATE,
#     "bjzxhy__test": 100_432,
#     "klds__test": 100_431,
# }
# gt_mode["default"] = gt_mode["vcode"]


def random_vcode(length: int = 6):
    chars = string.digits
    return "".join(random.choice(chars) for _ in range(length))


RE_PHONE = re.compile(r"1\d{10,10}$")


def send_vcode(
    phone: Union[str, int],
    vcode: Union[str, int, None] = None,
    template: Union[str, int, None] = None,
) -> str:
    """发送验证码
    vcode不传的话，会自动生成随机的6为整数，发送成功返回该整数；
    手机号不符规则会抛出PhoneValidationError，发送失败抛出SmsSendFail
    Example::
        from toolbox import send_vcode
        phone = 18620610600
        vcode = send_vcode(phone)
        assert len(vcode) == 6 and vcode.isdigit()
    """
    if settings.DEBUG:
        return "123456"
    phone = str(phone).replace(" ", "")
    if phone.startswith("133205020"):  # this type of phone number is for test
        phone = "13392809165"  # LF's phone
    elif phone.startswith("123456789"):
        phone = "18620610600"  # send to WJ for tech department debug purpose.
    if vcode is None:
        code = random_vcode()
    else:
        code = str(vcode)
    if template is None:
        template = ZHSQKJ_VCODE_TEMPLATE
    send_sms(phone, template, code=code)
    return code


def send_sms(
    phone: Union[str, int], template: Union[int, str], **kwargs: str
) -> None:
    """send sms to phone using the template

    :param phone: the phone number to receive sms.
    :param template: template id that provided by gt.
    :param kwargs: kwargs that the template need.
    """
    phone = str(phone).replace(" ", "")
    if not RE_PHONE.match(phone):
        raise PhoneValidationError
    try:
        mode_id = int(template)
    except ValueError:
        raise SmsSendFail("请传入正确的模版ID")
    max_length = MAX_VAR_VALUE_LENGTH  # 忽略验证短信的情况
    if any(len(str(v)) > max_length for k, v in kwargs.items()):
        raise SmsSendFail(f"变量值不能超过{max_length}个字符")
    data = {"phone": phone, "modeId": mode_id, "arguments": kwargs}
    r = requests.post(HOST, json=data, headers=gen_headers())
    if not r.ok or r.json().get("status") != 200:
        raise SmsSendFail(r.text)
